<template>
  <div class="slider-verify" :style="{ width: canvasWidth + 'px' }" onselectstart="return false;">
    <!-- 图片加载遮蔽罩 -->
    <div class="slider-verify__loading" :style="{ height: canvasHeight + 'px' }" v-if="isLoading">
      <div class="slider-verify__loading-spinner"></div>
    </div>

    <!-- 认证成功后的文字提示 -->
    <div class="slider-verify__success" :style="{ height: canvasHeight + 'px' }" v-if="verifySuccess">
      <i class="slider-verify__success-icon"></i>
      <span>{{ successHint }}</span>
    </div>

    <!--刷新按钮-->
    <div class="slider-verify__refresh" @click="refresh" title="刷新验证码">
      <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none">
        <path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" />
        <path d="M21 3v5h-5" />
      </svg>
    </div>

    <!--前端生成-->
    <template v-if="isFrontCheck">
      <!--验证图片-->
      <canvas ref="canvas" class="slider-verify__canvas" :width="canvasWidth" :height="canvasHeight" />
      <!--阻塞块-->
      <canvas ref="block" class="slider-verify__block" :class="{ 'is-fail': verifyFail, 'is-active': verifyActive }"
        :width="canvasWidth" :height="canvasHeight" />
    </template>

    <!--后端生成-->
    <template v-else>
      <!--验证图片-->
      <img ref="canvas" class="slider-verify__canvas" :width="canvasWidth" :height="canvasHeight" />
      <!--阻塞块-->
      <img ref="block" class="slider-verify__block" :class="{ 'is-fail': verifyFail, 'is-active': verifyActive }" />
    </template>

    <!-- 滑动条 -->
    <div class="slider-verify__slider" :class="{
      'is-active': verifyActive,
      'is-success': verifySuccess,
      'is-fail': verifyFail,
    }">
      <!--滑块进度条-->
      <div class="slider-verify__progress" :style="{ width: sliderBoxWidth + 'px' }">
        <div class="slider-verify__progress-shine"></div>
        <span class="slider-verify__success-text" v-if="verifySuccess">验证通过</span>
      </div>

      <!-- 按钮 -->
      <div class="slider-verify__button" id="slider-button"
        :style="{ transform: `translateX(${sliderButtonTranslate})` }">
        <!-- 按钮图标 -->
        <div class="slider-verify__button-icon"></div>
      </div>

      <!--滑动条提示文字-->
      <span class="slider-verify__hint">{{ sliderHint }}</span>
    </div>
  </div>
</template>

<script>
function sum(x, y) {
  return x + y;
}

function square(x) {
  return x * x;
}

import { getCaptchaApi } from "@/api/auth";
export default {
  name: "sliderVerify",
  props: {
    // 阻塞块长度
    blockLength: {
      type: Number,
      default: 42,
    },
    // 阻塞块弧度
    blockRadius: {
      type: Number,
      default: 10,
    },
    // 画布宽度
    canvasWidth: {
      type: Number,
      default: 320,
    },
    // 画布高度
    canvasHeight: {
      type: Number,
      default: 155,
    },
    // 滑块操作提示
    sliderHint: {
      type: String,
      default: "向右滑动验证",
    },
    // 可允许的误差范围小；为1时，则表示滑块要与凹槽完全重叠，才能验证成功。默认值为3
    accuracy: {
      type: Number,
      default: 3,
    },
    // 图片资源数组
    imageList: {
      type: Array,
      default: () => [],
    },
    // 是否使用惯性滑动
    useInertia: {
      type: Boolean,
      default: true
    },
    // 超时时间（秒）
    timeout: {
      type: Number,
      default: 10
    }
  },
  data() {
    return {
      // 前端校验
      isFrontCheck: false,
      // 校验进行状态
      verifyActive: false,
      // 校验成功状态
      verifySuccess: false,
      // 校验失败状态
      verifyFail: false,
      // 阻塞块对象
      blockObj: null,
      // 图片画布对象
      canvasCtx: null,
      // 阻塞块画布对象
      blockCtx: null,
      // 阻塞块宽度
      blockWidth: this.blockLength * 2,
      // 阻塞块的横轴坐标
      blockX: undefined,
      // 阻塞块的纵轴坐标
      blockY: undefined,
      // 图片对象
      image: undefined,
      // 移动的X轴坐标
      originX: undefined,
      // 移动的Y轴做坐标
      originY: undefined,
      // 拖动距离数组
      dragDistanceList: [],
      // 滑块箱拖动宽度
      sliderBoxWidth: 0,
      // 滑块按钮位移量
      sliderButtonTranslate: "0px",
      // 鼠标按下状态
      isMouseDown: false,
      // 图片加载提示，防止图片没加载完就开始验证
      isLoading: true,
      // 时间戳，计算滑动时长
      timestamp: null,
      // 成功提示
      successHint: "",
      // 随机字符串
      nonceStr: undefined,
      // 惯性动画 ID
      inertiaAnimationId: null,
      // 速度
      velocity: 0,
      // 上一次移动时间
      lastMoveTime: 0,
      // 上一次移动位置
      lastMoveX: 0,
      // 设备类型
      isMobile: false,
      // 重置超时
      resetTimeout: null,
      // DOM 元素引用
      sliderButton: null,
      sliderProgress: null,
      blockElement: null,
    };
  },
  mounted() {
    this.init();
    // 检测设备类型
    this.isMobile = this.checkIsMobile();
    // 监听窗口大小变化，自适应移动端
    window.addEventListener('resize', this.handleResize);
  },
  beforeDestroy() {
    // 移除事件监听和动画
    this.removeEvents();
    window.removeEventListener('resize', this.handleResize);
    this.cancelInertiaAnimation();

    // 重置所有状态，避免内存泄漏
    if (this.$refs.canvas) {
      this.$refs.canvas.onload = null;
    }

    // 清空对象引用
    this.blockObj = null;
    this.canvasCtx = null;
    this.blockCtx = null;
    this.image = null;

    // 清除定时器
    clearTimeout(this.resetTimeout);
  },
  methods: {
    /* 检测是否为移动设备 */
    checkIsMobile() {
      return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    },

    /* 处理窗口大小变化 */
    handleResize() {
      // 如果在操作中，不进行处理
      if (this.isMouseDown || this.verifySuccess) return;

      // 重置滑块位置
      this.resetSlider();
    },

    /* 重置滑块位置 */
    resetSlider() {
      // 添加过渡效果
      if (this.blockElement) {
        this.blockElement.style.transition = "left 0.3s ease";
        this.blockElement.style.left = 0;
      }

      this.sliderBoxWidth = 0;
      this.sliderButtonTranslate = "0px";

      // 清除之前的超时
      clearTimeout(this.resetTimeout);

      // 重置过渡效果
      this.resetTimeout = setTimeout(() => {
        if (this.blockElement) {
          this.blockElement.style.transition = "";
        }
      }, 300);
    },

    /* 初始化*/
    init() {
      this.initDom();
      this.bindEvents();
    },

    /* 初始化DOM对象*/
    initDom() {
      this.blockElement = this.$refs.block;
      this.sliderButton = document.getElementById("slider-button");
      this.sliderProgress = this.$el.querySelector('.slider-verify__progress');
      if (this.isFrontCheck) {
        this.canvasCtx = this.$refs.canvas.getContext("2d");
        this.blockCtx = this.blockElement.getContext("2d");
        this.initImage();
      } else {
        this.getCaptcha();
      }
    },

    /* 后台获取验证码*/
    getCaptcha() {
      //取后端默认值
      const data = {};
      this.isLoading = true;

      getCaptchaApi(data)
        .then((response) => {
          const data = response.data;
          this.nonceStr = data.nonceStr;
          this.$refs.block.src = data.blockSrc;
          this.$refs.block.style.top = data.blockY + "px";
          this.$refs.canvas.src = data.canvasSrc;

          // 图片加载完成后立即关闭遮蔽罩
          this.$refs.canvas.onload = () => {
            this.isLoading = false;
          };
        })
        .catch(() => {
          this.isLoading = false;
        });
    },

    /* 前端获取验证码*/
    initImage() {
      const image = this.createImage(() => {
        this.drawBlock();
        let {
          canvasWidth,
          canvasHeight,
          blockX,
          blockY,
          blockRadius,
          blockWidth,
        } = this;
        this.canvasCtx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
        this.blockCtx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
        // 将抠图防止最左边位置
        let yAxle = blockY - blockRadius * 2;
        let ImageData = this.blockCtx.getImageData(
          blockX,
          yAxle,
          blockWidth,
          blockWidth
        );
        this.blockElement.width = blockWidth;
        this.blockCtx.putImageData(ImageData, 0, yAxle);

        // 图片加载完立即关闭遮蔽罩
        this.isLoading = false;

        // 前端校验设置特殊值
        this.nonceStr = "loyer";
      });
      this.image = image;
    },

    /* 创建image对象*/
    createImage(onload) {
      const image = document.createElement("img");
      image.crossOrigin = "Anonymous";
      image.onload = onload;
      image.onerror = () => {
        // 加载失败时使用备用图片
        image.src = this.getFallbackImage();
      };
      image.src = this.getImageSrc();
      return image;
    },

    /* 获取备用图片 */
    getFallbackImage() {
      return `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="320" height="160" viewBox="0 0 320 160"><rect width="320" height="160" fill="%23f0f2f5"/><text x="50%" y="50%" font-family="Arial" font-size="24" fill="%23999" text-anchor="middle">图片加载失败</text></svg>`;
    },

    /* 获取imgSrc*/
    getImageSrc() {
      const len = this.imageList.length;
      return len > 0
        ? this.imageList[this.getNonceByRange(0, len)]
        : `https://loyer.wang/view/ftp/wallpaper/${this.getNonceByRange(1, 1000)}.jpg`;
    },

    /* 根据指定范围获取随机数*/
    getNonceByRange(start, end) {
      return Math.round(Math.random() * (end - start) + start);
    },

    /* 绘制阻塞块*/
    drawBlock() {
      this.blockX = this.getNonceByRange(
        this.blockWidth + 10,
        this.canvasWidth - (this.blockWidth + 10)
      );
      this.blockY = this.getNonceByRange(
        10 + this.blockRadius * 2,
        this.canvasHeight - (this.blockWidth + 10)
      );
      this.draw(this.canvasCtx, "fill");
      this.draw(this.blockCtx, "clip");
    },

    /* 绘制事件*/
    draw(ctx, operation) {
      const PI = Math.PI;
      let { blockX: x, blockY: y, blockLength: l, blockRadius: r } = this;
      // 绘制
      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
      ctx.lineTo(x + l, y);
      ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
      ctx.lineTo(x + l, y + l);
      ctx.lineTo(x, y + l);
      ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
      ctx.lineTo(x, y);
      // 修饰
      ctx.lineWidth = 2;
      ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
      ctx.strokeStyle = "rgba(255, 255, 255, 0.9)";
      ctx.stroke();
      ctx[operation]();
      ctx.globalCompositeOperation = "destination-over";
    },

    /* 取消惯性动画 */
    cancelInertiaAnimation() {
      if (this.inertiaAnimationId) {
        cancelAnimationFrame(this.inertiaAnimationId);
        this.inertiaAnimationId = null;
      }
    },

    /* 惯性滑动效果 */
    applyInertia(currentX) {
      // 仅在配置了惯性且不在移动设备上应用惯性
      if (!this.useInertia || this.isMobile) return;

      const now = Date.now();
      const deltaTime = now - this.lastMoveTime;

      if (deltaTime > 0 && this.lastMoveTime !== 0) {
        // 计算当前速度，调整为更平滑的手感
        this.velocity = (currentX - this.lastMoveX) / deltaTime * 10; // 降低速度因子，使惯性更柔和
      }

      this.lastMoveTime = now;
      this.lastMoveX = currentX;
    },

    /* 开始惯性动画 */
    startInertiaAnimation() {
      // 如果没有速度或者不使用惯性，则不执行动画
      if (Math.abs(this.velocity) < 0.1 || !this.useInertia || this.isMobile) return;

      // 增强的阻尼系数
      const friction = 0.95; // 增加阻尼，减速更平滑
      const minVelocity = 0.1;
      const maxMoveX = this.canvasWidth - 40;

      const animate = () => {
        // 应用摩擦力
        this.velocity *= friction;

        // 当前移动量
        const moveX = parseFloat(this.sliderButtonTranslate) + this.velocity;

        // 边界检查
        if (moveX <= 0) {
          this.updateSliderPosition(0);
          this.cancelInertiaAnimation();
          return;
        }

        if (moveX >= maxMoveX) {
          this.updateSliderPosition(maxMoveX);
          this.cancelInertiaAnimation();
          return;
        }

        // 更新位置
        this.updateSliderPosition(moveX);

        // 如果速度足够小，停止动画
        if (Math.abs(this.velocity) < minVelocity) {
          this.cancelInertiaAnimation();
          return;
        }

        this.inertiaAnimationId = requestAnimationFrame(animate);
      };

      // 启动动画
      this.inertiaAnimationId = requestAnimationFrame(animate);
    },

    /* 事件绑定*/
    bindEvents() {
      // 监听鼠标按下事件
      this.sliderButton.addEventListener("mousedown", this.handleStart);

      // 监听触摸开始事件
      this.sliderButton.addEventListener("touchstart", this.handleStart, { passive: false });

      // 监听鼠标移动事件
      document.addEventListener("mousemove", this.handleMove);

      // 监听触摸滑动事件
      document.addEventListener("touchmove", this.handleMove, { passive: false });

      // 监听鼠标离开事件
      document.addEventListener("mouseup", this.handleEnd);

      // 监听触摸离开事件
      document.addEventListener("touchend", this.handleEnd);
    },

    /* 移除事件监听 */
    removeEvents() {
      if (this.sliderButton) {
        this.sliderButton.removeEventListener("mousedown", this.handleStart);
        this.sliderButton.removeEventListener("touchstart", this.handleStart);
      }

      document.removeEventListener("mousemove", this.handleMove);
      document.removeEventListener("touchmove", this.handleMove);
      document.removeEventListener("mouseup", this.handleEnd);
      document.removeEventListener("touchend", this.handleEnd);
    },

    /* 事件开始处理函数 */
    handleStart(event) {
      event.preventDefault();
      const clientX = event.clientX || (event.touches && event.touches[0].clientX);
      const clientY = event.clientY || (event.touches && event.touches[0].clientY);

      this.startEvent(clientX, clientY);
    },

    /* 事件移动处理函数 */
    handleMove(event) {
      if (!this.isMouseDown) return;

      event.preventDefault();
      const clientX = event.clientX || (event.touches && event.touches[0].clientX);
      const clientY = event.clientY || (event.touches && event.touches[0].clientY);

      this.moveEvent(clientX, clientY);
    },

    /* 事件结束处理函数 */
    handleEnd(event) {
      if (!this.isMouseDown) return;

      const clientX = event.clientX || (event.changedTouches && event.changedTouches[0].clientX);
      this.endEvent(clientX);
    },

    /* 校验图片是否存在*/
    checkImgSrc() {
      if (this.isFrontCheck) {
        return true;
      }
      return !!this.$refs.canvas.src;
    },

    /* 滑动开始事件*/
    startEvent(originX, originY) {
      if (!this.checkImgSrc() || this.isLoading || this.verifySuccess) {
        return;
      }

      // 取消任何正在进行的惯性动画
      this.cancelInertiaAnimation();

      this.originX = originX;
      this.originY = originY;
      this.isMouseDown = true;
      this.timestamp = Date.now();

      // 初始化惯性滑动参数
      this.velocity = 0;
      this.lastMoveTime = 0;
      this.lastMoveX = 0;

      // 添加活动状态样式
      this.verifyActive = true;

      // 清空拖动距离记录
      this.dragDistanceList = [];
    },

    /* 滑动事件*/
    moveEvent(clientX, clientY) {
      if (!this.isMouseDown) {
        return false;
      }

      // 使用requestAnimationFrame确保更平滑的动画
      requestAnimationFrame(() => {
        const moveX = Math.min(Math.max(0, clientX - this.originX), this.canvasWidth - 40);
        const moveY = clientY - this.originY;

        // 直接更新 DOM，提升性能
        this.updateSliderPosition(moveX);

        // 记录Y轴移动距离用于图灵测试
        this.dragDistanceList.push(moveY);

        // 应用惯性
        this.applyInertia(moveX);
      });
    },

    /* 滑动结束事件*/
    endEvent(clientX) {
      if (!this.isMouseDown) {
        return false;
      }

      this.isMouseDown = false;
      this.verifyActive = false;

      // 如果没有移动，直接返回
      if (clientX === this.originX) {
        return false;
      }

      // 开始惯性滑动
      this.startInertiaAnimation();

      // 计算滑动时长（毫秒）
      const elapsedTime = Date.now() - this.timestamp;

      // 移动距离
      const moveLength = parseInt(this.blockElement.style.left);

      // 限制操作时长，超出判断失败
      if (elapsedTime > this.timeout * 1000) {
        this.verifyFailEvent("验证超时，请重试");
        return;
      }

      // 人为操作判定
      if (!this.turingTest()) {
        this.verifyFailEvent("验证失败，请重试");
        return;
      }

      // 开始校验
      this.isLoading = true;

      // 是否前端校验
      if (this.isFrontCheck) {
        const accuracy = Math.min(Math.max(this.accuracy, 1), 10); // 容错精度值
        const spliced = Math.abs(moveLength - this.blockX) <= accuracy; // 判断是否重合

        // 立即验证，移除延迟
        if (!spliced) {
          this.verifyFailEvent();
        } else {
          // 设置特殊值，后台特殊处理，直接验证通过
          this.$emit("success", { nonceStr: this.nonceStr, value: moveLength });
        }
      } else {
        // 立即验证，移除延迟
        this.$emit("success", { nonceStr: this.nonceStr, value: moveLength });
      }
    },

    /* 图灵测试*/
    turingTest() {
      const arr = this.dragDistanceList; // 拖动距离数组

      // 如果记录点太少，可能是机器操作
      if (arr.length < 5) return false;

      const average = arr.reduce(sum) / arr.length; // 平均值
      const deviations = arr.map((x) => x - average); // 偏离值
      const stdDev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); // 标准偏差

      // 人为操作应有波动，标准偏差不应太小
      return stdDev > 0.5 && average !== stdDev;
    },

    /* 校验成功*/
    verifySuccessEvent() {
      this.isLoading = false;
      this.verifySuccess = true;

      // 为根元素添加成功状态类
      this.$el.classList.add('is-success-state');

      // 取消惯性动画
      this.cancelInertiaAnimation();

      // 计算用时
      const elapsedTime = ((Date.now() - this.timestamp) / 1000).toFixed(1);

      if (elapsedTime < 1) {
        this.successHint = `仅用${elapsedTime}秒，速度快如闪电✨`;
      } else if (elapsedTime < 2) {
        this.successHint = `${elapsedTime}秒完成，操作很流畅👍`;
      } else {
        this.successHint = `用时${elapsedTime}秒，验证通过✅`;
      }

      // 成功时添加匹配动画效果
      if (this.blockElement) {
        this.blockElement.style.transition = "left 0.3s ease, transform 0.5s ease, box-shadow 0.5s ease";
        this.blockElement.classList.add("is-success");
      }

      // 震动反馈（仅移动设备）
      if (this.isMobile && navigator.vibrate) {
        navigator.vibrate(100);
      }
    },

    /* 校验失败*/
    verifyFailEvent(msg) {
      // 取消惯性动画
      this.cancelInertiaAnimation();

      // 震动反馈（仅移动设备）
      if (this.isMobile && navigator.vibrate) {
        navigator.vibrate([50, 50, 50]);
      }

      // 判断错误类型，如果是登录错误（非验证错误）
      const isLoginError = msg && (
        msg.includes("用户不存在") ||
        msg.includes("密码错误") ||
        msg.includes("用户名不正确") ||
        msg.includes("用户名或密码错误") ||
        msg.includes("账号不存在") ||
        msg.includes("账号或密码")
      );

      // 如果是登录错误，发送特殊事件让父组件处理
      if (isLoginError) {
        this.$emit("login_error", msg);
        return;
      }

      // 设置失败状态，但只短暂显示
      this.verifyFail = true;

      // 立即触发失败事件
      this.$emit("fail", msg);

      // 短暂显示失败状态后重新获取验证码 - 缩短延迟时间
      setTimeout(() => {
        this.refresh();
      }, 500);
    },

    /* 外部调用：触发失败 */
    triggerFail(msg) {
      this.verifyFailEvent(msg);
    },

    /* 刷新图片验证码*/
    refresh() {
      // 移除成功状态类
      this.$el.classList.remove('is-success-state');
      // 取消惯性动画
      this.cancelInertiaAnimation();

      // 重置各种状态
      this.verifyFail = false;
      this.isLoading = true;
      this.verifyActive = false;
      this.verifySuccess = false;
      this.resetSlider();
      this.dragDistanceList = [];
      this.velocity = 0;
      this.lastMoveTime = 0;
      this.lastMoveX = 0;

      if (this.isFrontCheck) {
        // 刷新画布
        let { canvasWidth, canvasHeight } = this;
        this.canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        this.blockCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        this.blockElement.width = canvasWidth;
        // 刷新图片
        this.image.src = this.getImageSrc();
      } else {
        this.getCaptcha();
      }
    },

    /* 更新滑块和拼图的位置 */
    updateSliderPosition(moveX) {
      // 使用 transform 实现更平滑的动画
      this.sliderButton.style.transform = `translateX(${moveX}px)`;
      this.sliderProgress.style.width = `${moveX}px`;

      // 计算拼图块位置 - 使用精确比例确保同步移动
      const blockLeft = ((this.canvasWidth - 40 - 20) / (this.canvasWidth - 40)) * moveX;
      this.blockElement.style.left = blockLeft + "px";

      // 更新 Vue data 以便其他逻辑使用
      this.sliderButtonTranslate = moveX + 'px';
      this.sliderBoxWidth = moveX;
    },
  },
};
</script>

<style scoped>
.slider-verify {
  position: relative;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  overflow: hidden;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  touch-action: none;
  /* 阻止浏览器默认触摸行为，优化移动端体验 */
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* 移除移动端点击高亮 */
  transform: translateZ(0);
  /* 启用GPU加速 */
  transition: all 0.3s ease;
  /* 添加边框效果 */
  border: 1px solid rgba(0, 0, 0, 0.05);
  background-color: #f7faff;
}

.slider-verify:hover {
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1), 0 0 1px rgba(64, 158, 255, 0.1);
  border-color: rgba(64, 158, 255, 0.3);
}

.slider-verify.is-success-state {
  border-color: rgba(40, 167, 69, 0.5);
  box-shadow: 0 8px 24px rgba(40, 167, 69, 0.15);
}

/* 图片加载样式 */
.slider-verify__loading {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  z-index: 999;
  background-color: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  animation: fadeIn 0.3s ease;
}

.slider-verify__loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(64, 158, 255, 0.2);
  border-left-color: #409eff;
  border-radius: 50%;
  animation: spin 1s infinite linear;
}

/* 认证成功后的文字提示 */
.slider-verify__success {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(8px);
  color: #28a745;
  font-size: 16px;
  font-weight: 500;
  border-radius: 12px;
  animation: successFadeIn 0.5s ease;
}

.slider-verify__success-icon {
  display: block;
  width: 50px;
  height: 50px;
  margin-bottom: 10px;
  border-radius: 50%;
  background-color: #28a745;
  position: relative;
  box-shadow: 0 6px 16px rgba(40, 167, 69, 0.4);
  animation: bounceIn 0.6s ease;
}

.slider-verify__success-icon:before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 25px;
  height: 12px;
  border-left: 4px solid #fff;
  border-bottom: 4px solid #fff;
  transform: translate(-50%, -60%) rotate(-45deg);
  animation: checkmark 0.4s ease-in-out 0.2s forwards;
}

@keyframes checkmark {
  0% {
    width: 0;
    height: 0;
    opacity: 0;
  }

  100% {
    width: 25px;
    height: 12px;
    opacity: 1;
  }
}

@keyframes bounceIn {
  0% {
    transform: scale(0);
  }

  50% {
    transform: scale(1.1);
  }

  70% {
    transform: scale(0.9);
  }

  100% {
    transform: scale(1);
  }
}

/* 刷新按钮 */
.slider-verify__refresh {
  position: absolute;
  right: 10px;
  top: 10px;
  width: 36px;
  height: 36px;
  cursor: pointer;
  background-color: rgba(0, 0, 0, 0.3);
  border-radius: 50%;
  z-index: 10;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  backdrop-filter: blur(2px);
  color: white;
}

.slider-verify__refresh:hover {
  transform: rotate(180deg) scale(1.1);
  background-color: #409eff;
  box-shadow: 0 0 12px rgba(64, 158, 255, 0.6);
}

.slider-verify__refresh:active svg {
  transform: scale(0.9);
}

.slider-verify__refresh svg {
  transition: transform 0.3s ease;
}

/* 验证图片 */
.slider-verify__canvas {
  border-radius: 12px 12px 0 0;
  display: block;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  transition: filter 0.3s ease;
  object-fit: cover;
  filter: brightness(1);
}

/* 阻塞块 */
.slider-verify__block {
  position: absolute;
  left: 0;
  top: 0;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.6), inset 0 0 3px rgba(255, 255, 255, 0.3);
  cursor: pointer;
  transition: all 0.2s ease-out;
  border-radius: 2px;
  z-index: 2;
  will-change: transform, left, box-shadow;
  backface-visibility: hidden;
}

/* 活动状态下的阻塞块样式 */
.slider-verify__block.is-active {
  transform: scale(1.1);
  box-shadow: 0 0 20px rgba(64, 158, 255, 0.8), 0 0 0 3px rgba(64, 158, 255, 0.6), inset 0 0 8px rgba(255, 255, 255, 0.5);
  filter: brightness(1.1);
}

/* 成功状态下的阻塞块样式 */
.slider-verify__block.is-success {
  transform: scale(1);
  box-shadow: 0 0 20px rgba(40, 167, 69, 0.7), 0 0 0 3px rgba(40, 167, 69, 0.5), inset 0 0 5px rgba(255, 255, 255, 0.5);
  animation: blockPulse 0.6s ease;
}

/* 校验失败时的阻塞块样式 */
.slider-verify__block.is-fail {
  transition: left 0.5s cubic-bezier(0.17, 0.89, 0.45, 1);
  animation: shake 0.6s cubic-bezier(.36, .07, .19, .97) both;
  box-shadow: 0 0 20px rgba(220, 53, 69, 0.8), 0 0 0 3px rgba(220, 53, 69, 0.6), inset 0 0 5px rgba(255, 255, 255, 0.3);
  filter: brightness(1.1);
}

/* 阻塞块匹配成功的脉冲动画 */
@keyframes blockPulse {
  0% {
    transform: scale(1);
    box-shadow: 0 0 15px rgba(40, 167, 69, 0.3), 0 0 0 1px rgba(40, 167, 69, 0.3), inset 0 0 3px rgba(255, 255, 255, 0.3);
  }

  50% {
    transform: scale(1.15);
    box-shadow: 0 0 25px rgba(40, 167, 69, 0.9), 0 0 0 4px rgba(40, 167, 69, 0.7), inset 0 0 10px rgba(255, 255, 255, 0.6);
  }

  100% {
    transform: scale(1);
    box-shadow: 0 0 20px rgba(40, 167, 69, 0.7), 0 0 0 3px rgba(40, 167, 69, 0.5), inset 0 0 5px rgba(255, 255, 255, 0.5);
  }
}

@keyframes shake {

  10%,
  90% {
    transform: translateX(-2px) scale(1.05);
  }

  20%,
  80% {
    transform: translateX(4px) scale(1.05);
  }

  30%,
  50%,
  70% {
    transform: translateX(-6px) scale(1.05);
  }

  40%,
  60% {
    transform: translateX(6px) scale(1.05);
  }
}

/* 滑动条 */
.slider-verify__slider {
  position: relative;
  width: 100%;
  height: 50px;
  background: #f0f2f5;
  color: #45494c;
  border: 0;
  border-radius: 0 0 12px 12px;
  transition: all 0.3s ease;
  overflow: hidden;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.08);
  background-image: linear-gradient(to bottom, #f7f9fa, #e9ecf0);
}

/* 滑动进度条 */
.slider-verify__progress {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background: linear-gradient(135deg, #a8d4ff, #409eff);
  border-radius: 0 25px 25px 0;
  transition: width 0.1s linear;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow: inset 0 -2px 5px rgba(0, 0, 0, 0.1);
  will-change: width;
  backface-visibility: hidden;
}

/* 进度条闪光效果 */
.slider-verify__progress-shine {
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 100%;
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0) 100%);
  animation: progressShine 2s infinite linear;
}

/* 进度条闪光动画 */
@keyframes progressShine {
  0% {
    transform: translateX(-100%);
  }

  100% {
    transform: translateX(50%);
  }
}

/* 成功文本 */
.slider-verify__success-text {
  color: #fff;
  font-weight: 500;
  opacity: 0;
  transform: translateY(20px);
  animation: slideUp 0.5s 0.2s forwards;
  letter-spacing: 2px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  position: relative;
  z-index: 2;
}

@keyframes slideUp {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 滑动按钮 */
.slider-verify__button {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 40px;
  height: 40px;
  background: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 -2px 4px rgba(0, 0, 0, 0.06);
  cursor: pointer;
  border-radius: 50%;
  transition: transform 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28), box-shadow 0.3s ease;
  z-index: 3;
  will-change: transform;
  backface-visibility: hidden;
  background-image: linear-gradient(to bottom, #ffffff, #f5f9ff);
}

.slider-verify__button:hover {
  transform: scale(1.05);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18), inset 0 -2px 4px rgba(0, 0, 0, 0.06);
}

/* 滑动按钮图标 */
.slider-verify__button-icon {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  overflow: hidden;
  transition: all 0.3s ease;
  background-image: linear-gradient(135deg, #409eff, #66b1ff);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.slider-verify__button-icon:after {
  content: '»';
  color: #fff;
  font-size: 22px;
  font-weight: bold;
  transition: all 0.3s ease;
  text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.15);
}

/* 活动状态 */
.slider-verify__slider.is-active .slider-verify__button {
  box-shadow: 0 0 20px rgba(64, 158, 255, 0.6), inset 0 -2px 4px rgba(0, 0, 0, 0.06);
  transform: scale(1.1);
}

.slider-verify__slider.is-active .slider-verify__progress {
  background: linear-gradient(135deg, #a8d4ff, #409eff);
  box-shadow: inset 0 -2px 5px rgba(0, 0, 0, 0.15);
}

.slider-verify__slider.is-active .slider-verify__button-icon {
  background-image: linear-gradient(135deg, #66b1ff, #409eff);
}

.slider-verify__slider.is-active .slider-verify__button-icon:after {
  color: #fff;
  animation: pulse 1s infinite;
}

/* 成功状态 */
.slider-verify__slider.is-success .slider-verify__progress {
  background: linear-gradient(135deg, #a8e6cf, #28a745);
  transition: all 0.5s cubic-bezier(0.17, 0.89, 0.45, 1);
  box-shadow: inset 0 -2px 5px rgba(0, 0, 0, 0.15);
}

.slider-verify__slider.is-success .slider-verify__progress-shine {
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.6) 50%, rgba(255, 255, 255, 0) 100%);
}

.slider-verify__slider.is-success .slider-verify__button {
  background-color: #28a745;
  background-image: linear-gradient(to bottom, #34d058, #28a745);
  box-shadow: 0 0 20px rgba(40, 167, 69, 0.6), inset 0 -2px 5px rgba(0, 0, 0, 0.1);
  transform: scale(1.05);
}

.slider-verify__slider.is-success .slider-verify__button-icon {
  background-image: linear-gradient(135deg, #34d058, #28a745);
}

.slider-verify__slider.is-success .slider-verify__button-icon:after {
  content: '✓';
  color: white;
  animation: successPulse 1s;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

/* 失败状态 */
.slider-verify__slider.is-fail .slider-verify__progress {
  background: linear-gradient(135deg, #f8d7da, #dc3545);
  transition: all 0.5s cubic-bezier(0.17, 0.89, 0.45, 1);
  box-shadow: inset 0 -2px 5px rgba(0, 0, 0, 0.15);
}

.slider-verify__slider.is-fail .slider-verify__button {
  background-color: #dc3545;
  background-image: linear-gradient(to bottom, #e45a67, #dc3545);
  box-shadow: 0 0 20px rgba(220, 53, 69, 0.6), inset 0 -2px 5px rgba(0, 0, 0, 0.1);
  animation: shake 0.6s cubic-bezier(.36, .07, .19, .97) both;
}

.slider-verify__slider.is-fail .slider-verify__button-icon {
  background-image: linear-gradient(135deg, #e45a67, #dc3545);
}

.slider-verify__slider.is-fail .slider-verify__button-icon:after {
  content: '✕';
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  color: #8892a0;
  font-size: 15px;
  letter-spacing: 1px;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
}

.slider-verify__hint {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  color: #8892a0;
  font-size: 15px;
  letter-spacing: 1px;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
}

.slider-verify__hint:before {
  content: '>>';
  display: inline-block;
  margin-right: 8px;
  animation: hintArrow 1.5s infinite;
  font-weight: bold;
  color: #409eff;
}

/* 活动、成功、失败状态下隐藏提示文字 */
.slider-verify__slider.is-active .slider-verify__hint,
.slider-verify__slider.is-success .slider-verify__hint,
.slider-verify__slider.is-fail .slider-verify__hint {
  opacity: 0;
  transform: translateY(10px);
}

/* 动画 */
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

@keyframes hintArrow {

  0%,
  100% {
    transform: translateX(0);
    opacity: 0.5;
  }

  50% {
    transform: translateX(6px);
    opacity: 1;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes successFadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 0.9;
  }

  50% {
    transform: scale(1.2);
    opacity: 1;
  }

  100% {
    transform: scale(1);
    opacity: 0.9;
  }
}

@keyframes successPulse {
  0% {
    transform: scale(0.5);
    opacity: 0;
  }

  50% {
    transform: scale(1.2);
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* 移动端优化 */
@media (max-width: 576px) {
  .slider-verify {
    max-width: 100%;
    margin: 0 auto;
  }

  .slider-verify__slider {
    height: 60px;
    /* 更大的触摸区域 */
  }

  .slider-verify__button {
    top: 10px;
    width: 40px;
    height: 40px;
  }

  .slider-verify__hint {
    font-size: 16px;
    /* 更大的字体 */
  }

  .slider-verify__refresh {
    width: 44px;
    height: 44px;
  }
}
</style>
